<template>
  <div class="container">
    <h3>vue+openlayers: 小车移动轨迹动画，带开始、暂停、结束动画</h3>
    <h4>
      <el-button type="success" size="mini" @click="start">开始</el-button>
      <el-button type="warning" size="mini" @click="pause">暂停</el-button>
      <el-button type="danger" size="mini" @click="end">结束</el-button>
    </h4>
    <div id="vue-openlayers"></div>
  </div>
</template>

<script>
import 'ol/ol.css';
import { Map, View } from 'ol';
import Tile from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';
import { Point, LineString, Circle, Polygon } from 'ol/geom';
import { Style, Fill, Stroke, Text, Icon } from 'ol/style';
import CircleStyle from 'ol/style/Circle';
export default {
  data() {
    return {
      map: null,
      dataSources: new VectorSource({
        wrapX: false
      }),
      lineFeature: null, // 轨迹线
      pointFeature: null,
      step1: 0,
      requestID: null,
      lineData: [
        [116, 39],
        [116.005, 39],
        [116.005, 39.015],
        [116.016, 39.018],
        [116.015, 39.005]
      ],
      passSource: new VectorSource({
        wrapX: false
      }),
    }
  },
  mounted() {
    this.initMap();
  },
  methods: {
    initMap() {
      let layer = new Tile({
        preload: Infinity, // 预加载级别
        source: new XYZ({
          url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7'
        })
      })
      let featureLayer = new VectorLayer({
        source: this.dataSources,
        style: this.featureStyle()
      })
      let passLayer = new VectorLayer({
        source: this.passSource,
        style: new Style({
          fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)'
          }),
          stroke: new Stroke({
            color: '#ffcc33',
            width: 2
          }),
        })
      })
      this.map = new Map({
        target: 'vue-openlayers',
        layers: [layer, featureLayer, passLayer],
        view: new View({
          center: [116.397411, 39.909186],
          zoom: 7,
          projection: 'EPSG:4326'
        })
      })
      this.showTrack(); // 显示轨迹
    },

    // 开始
    start() {
      this.fit();
      cancelAnimationFrame(this.requestID);
      this.animation(this.step1);
    },

    // 暂停
    pause() {
      cancelAnimationFrame(this.requestID); // 取消动画 cancelAnimationFrame是window对象的方法
    },

    // 结束
    end() {
      this.step1 = 0;
      this.passSource.clear();
      cancelAnimationFrame(this.requestID);
      this.pointFeature.getGeometry().setCoordinates(this.lineFeature.getGeometry().getCoordinateAt(this.step1))
      this.pointFeature.getStyle().getImage().setRotation(0)
    },

    // 轨迹样式
    featureStyle() {
      let style = new Style({
        fill: new Fill({
          color: 'darkBlue'
        }),
        stroke: new Stroke({
          color: 'skyBlue',
          width: 2
        }),
      })
      return style;
    },

    fit() {
      // 地图跟随定位点移动缩放
      this.map.getView().fit(this.lineFeature.getGeometry(), {
        size: this.map.getSize(),
        padding: [50, 50, 50, 50],
        maxZoom: 18,
        duration: 1000
      })
    },

    showTrack() {
      this.lineFeature = new Feature({
        geometry: new LineString(this.lineData) // 轨迹线
      })
      this.dataSources.addFeature(this.lineFeature); // 添加轨迹线
      this.pointFeature = new Feature({
        geometry: new Point(this.lineData[0]) // 轨迹点
      })
      this.pointFeature.setStyle(
        new Style({
          image: new Icon({
            src: require('@/assets/car1.png'),
            rotateWithView: true, // 图标随地图旋转
            scale: 1.8 // 图标缩放比例
          }),
          zIndex: 10 // 图层层级
        })
      )
      this.dataSources.addFeature(this.pointFeature); // 添加轨迹点
      this.fit()
    },

    // 动画
    animation(step) {
      this.requestID = window.requestAnimationFrame(() => { // window.requestAnimationFrame是浏览器用于定时循环操作的一个接口，类似于setTimeout，主要用途是按帧对网页进行重绘
        if(step <= 1) { // 判断小车是否到达终点 1为终点
          let second = this.lineFeature.getGeometry().getCoordinateAt(step); // 获取轨迹线上的坐标
          let first = this.pointFeature.getGeometry().getCoordinates(); // 获取小车的坐标
          let angle = -Math.atan2(second[1] - first[1], second[0] - first[0]); // 获取小车与轨迹线的夹角
          // 重新设置小车的位置和角度
          this.pointFeature.getGeometry().setCoordinates(second);
          this.pointFeature.getStyle().getImage().setRotation(angle); // 设置小车的角度
          let coord = []
          coord.push(first)
          coord.push(second)
          let Tempfeature = new Feature({
            geometry: new LineString(coord) // 轨迹线
          })
          this.passSource.addFeature(Tempfeature); // 添加轨迹线
          step += 0.002; // 设置小车的速度
          this.step1 = step;
          this.animation(this.step1); // 递归调用
        } else { // 小车到达终点
          // 小车回到起点
          this.pointFeature.getGeometry().setCoordinates(this.lineData[0])
          this.pointFeature.getStyle().getImage().setRotation(0)
          let coord = []
          coord.push(this.pointFeature.getGeometry().getCoordinates())
          coord.push(this.lineData[0])
          let Tempfeature = new Feature({
            geometry: new LineString(coord) // 轨迹线
          })
          this.passSource.addFeature(Tempfeature); // 添加轨迹线
          this.end()
        }
      }, 1)
    }
  }
}
</script>

<style lang="scss" scoped>
.container {
    width: 100%;
    height: 750px;
    margin: 50px auto;
    border: 1px solid #ccc;
    #vue-openlayers {
      width: 100%;
      height: 720px;
      margin: 0 auto;
      border: 1px solid #42b983;
      position: relative;
    }
  }
</style>
